#include "DXUT.h"
#include "D3DUtilityFunction.h"

CD3DUtilityFunction::CD3DUtilityFunction(void)
{
}


CD3DUtilityFunction::~CD3DUtilityFunction(void)
{
}

//--------------------------------------------------------------------------------------
// Helper for compiling shaders with D3DX11
//--------------------------------------------------------------------------------------
HRESULT CD3DUtilityFunction::CompileShaderFromFile( WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut )
{
    HRESULT hr = S_OK;

    DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
    // Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
    // Setting this flag improves the shader debugging experience, but still allows 
    // the shaders to be optimized and to run exactly the way they will run in 
    // the release configuration of this program.
    dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif

    ID3DBlob* pErrorBlob;
    hr = D3DX11CompileFromFile( szFileName, NULL, NULL, szEntryPoint, szShaderModel, 
        dwShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL );
    if( FAILED(hr) )
    {
        if( pErrorBlob != NULL )
            OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() );
        if( pErrorBlob ) pErrorBlob->Release();
        return hr;
    }
    if( pErrorBlob ) pErrorBlob->Release();

    return S_OK;
}

HRESULT CD3DUtilityFunction::CreateEffectsFromFile(ID3D11Device* pd3dDevice, WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DX11Effect** ppEffect )
{
	HRESULT hr = S_OK;
	// Compile and create the effect.
    ID3DBlob* pEffectBuffer = NULL;

    V_RETURN( CompileShaderFromFile( szFileName, NULL, szShaderModel, &pEffectBuffer ) );
    V_RETURN( D3DX11CreateEffectFromMemory( pEffectBuffer->GetBufferPointer(),
                                            pEffectBuffer->GetBufferSize(),
                                            0,
                                            pd3dDevice,
                                            ppEffect ) );
	SAFE_RELEASE( pEffectBuffer );
	return hr;
}
//--------------------------------------------------------------------------------------
// Filling Vector3
//--------------------------------------------------------------------------------------
HRESULT CD3DUtilityFunction::FillVec3(float x, float y, float z, D3DXVECTOR3* pDst)
{
	if(pDst == NULL)
		return E_FAIL;
	pDst->x = x;
	pDst->y = y;
	pDst->z = z;
	return S_OK;
}
//--------------------------------------------------------------------------------------
// Create VB whose data type is just Vector3
//--------------------------------------------------------------------------------------
HRESULT CD3DUtilityFunction::CreateVBVec3(ID3D11Device* pd3dDevice, int iDataWidth, int iDataHeight, 
	float fWidthLength, float fHeightLength, float fStartOffsetX, float fStartOffsetY, float* pSrcDataBuf, ID3D11Buffer** ppVB)
{
	HRESULT hr = S_OK;
	//create vertex buffer
	float fStepX = fWidthLength/(iDataWidth-1);
	float fStepY = fHeightLength/(iDataHeight-1);
	float fStartX = fStartOffsetX*fWidthLength;
	float fStartY = fStartOffsetY*fHeightLength;
	D3DXVECTOR3* pVBData = new D3DXVECTOR3[iDataWidth*iDataHeight];
	for(int j = 0; j < iDataHeight; j++)
		for(int i = 0; i < iDataWidth; i++)
		{
			int idx = j*iDataWidth + i;
			FillVec3(i*fStepX+fStartX, j*fStepY+fStartY, pSrcDataBuf[idx], pVBData+idx);			
		}
	// Fill in a buffer description.
	D3D11_BUFFER_DESC bufferDesc;
	bufferDesc.Usage            = D3D11_USAGE_DEFAULT;
	bufferDesc.ByteWidth        = sizeof(D3DXVECTOR3) * (iDataWidth*iDataHeight);
	bufferDesc.BindFlags        = D3D11_BIND_VERTEX_BUFFER;
	bufferDesc.CPUAccessFlags   = 0;
	bufferDesc.MiscFlags        = 0;
	// Fill in the subresource data.
	D3D11_SUBRESOURCE_DATA InitData;
	InitData.pSysMem = pVBData;
	InitData.SysMemPitch = 0;
	InitData.SysMemSlicePitch = 0;
	// Create the vertex buffer.
	SAFE_RELEASE(*ppVB);
	V(pd3dDevice->CreateBuffer( &bufferDesc, &InitData, ppVB ));
	//for(int j = 0; j < iDataHeight; j++)
	//	for(int i = 0; i < iDataWidth; i++)
	//	{
	//		int idx = j*iDataWidth + i;
	//		printf("(%f,%f,%f)",(pVBData+idx)->x,(pVBData+idx)->y,(pVBData+idx)->z);
	//	}
	SAFE_DELETE_ARRAY(pVBData);
	return S_OK;
}

//--------------------------------------------------------------------------------------
// Create Vertex Buffer -
//-------------------------------------------------------------------------------------
HRESULT CD3DUtilityFunction::CreateVertexBuffer(ID3D11Device* pd3dDevice, int iStride, float* pSrcDataBuf, int iNumVertices, ID3D11Buffer** ppVB)
{
	HRESULT hr = S_OK;
	//create vertex buffer
	// Fill in a buffer description.
	D3D11_BUFFER_DESC bufferDesc;
	bufferDesc.Usage            = D3D11_USAGE_DEFAULT;
	bufferDesc.ByteWidth        = iStride * iNumVertices;
	bufferDesc.BindFlags        = D3D11_BIND_VERTEX_BUFFER;
	bufferDesc.CPUAccessFlags   = 0;
	bufferDesc.MiscFlags        = 0;
	// Fill in the subresource data.
	D3D11_SUBRESOURCE_DATA InitData;
	InitData.pSysMem = pSrcDataBuf;
	InitData.SysMemPitch = 0;
	InitData.SysMemSlicePitch = 0;
	// Create the vertex buffer.
	SAFE_RELEASE(*ppVB);
	hr = pd3dDevice->CreateBuffer( &bufferDesc, &InitData, ppVB );
	return hr;
}

//--------------------------------------------------------------------------------------
// Create index buffer for Grid plane
//--------------------------------------------------------------------------------------
HRESULT CD3DUtilityFunction::CreateIBGrid32Bit(ID3D11Device* pd3dDevice, int iDataWidth, int iDataHeight, ID3D11Buffer** ppIB)
{
	HRESULT hr = S_OK;
	unsigned int* pIBData = new unsigned int[(iDataWidth-1)*(iDataHeight-1)*6];
	for(int j = 0; j <(iDataHeight-1); j++)
		for(int i = 0; i <(iDataWidth-1); i++)
		{
			unsigned int idx = (j*(iDataWidth-1)+i)*6;
			unsigned int v1 = j*iDataWidth+i;
			unsigned int v2 = v1 + 1;
			unsigned int v3 = v1 + iDataWidth;
			unsigned int v4 = v2 + iDataWidth;
 			//first triangle
			pIBData[idx++] = v1; pIBData[idx++] = v2; pIBData[idx++] = v4;
			//second triangle
			pIBData[idx++] = v1; pIBData[idx++] = v4; pIBData[idx++] = v3;

			//printf("(%d,%d,%d,%d,%d,%d)\n",v1,v3,v2,v3,v4,v2);
		}

	D3D11_BUFFER_DESC bufferDesc;
    bufferDesc.Usage           = D3D11_USAGE_DEFAULT;
    bufferDesc.ByteWidth       = sizeof( unsigned int ) * (iDataWidth-1)*(iDataHeight-1)*6;
    bufferDesc.BindFlags       = D3D11_BIND_INDEX_BUFFER;
    bufferDesc.CPUAccessFlags  = 0;
    bufferDesc.MiscFlags       = 0;

    D3D11_SUBRESOURCE_DATA InitData;
    InitData.pSysMem = pIBData;
    InitData.SysMemPitch = 0;
    InitData.SysMemSlicePitch = 0;
	
	SAFE_RELEASE(*ppIB);
    V(hr = pd3dDevice->CreateBuffer( &bufferDesc, &InitData, ppIB));
	SAFE_DELETE_ARRAY(pIBData);

	return hr;
}

//--------------------------------------------------------------------------------------
// Create index buffer
//--------------------------------------------------------------------------------------
HRESULT CD3DUtilityFunction::CreateIndexBuffer(ID3D11Device* pd3dDevice, int iNumofIndices, void* pIndicesBuf, ID3D11Buffer** ppIB, bool b32Bit)
{
	HRESULT hr = S_OK;
	D3D11_BUFFER_DESC bufferDesc;
	bufferDesc.Usage           = D3D11_USAGE_DEFAULT;
	if(b32Bit)
		bufferDesc.ByteWidth       = sizeof( unsigned int ) * iNumofIndices;
	else
		bufferDesc.ByteWidth       = sizeof( unsigned short) * iNumofIndices;

	bufferDesc.BindFlags       = D3D11_BIND_INDEX_BUFFER;
	bufferDesc.CPUAccessFlags  = 0;
	bufferDesc.MiscFlags       = 0;

	D3D11_SUBRESOURCE_DATA InitData;
	InitData.pSysMem = pIndicesBuf;
	InitData.SysMemPitch = 0;
	InitData.SysMemSlicePitch = 0;
	
	SAFE_RELEASE(*ppIB);
	V(hr = pd3dDevice->CreateBuffer( &bufferDesc, &InitData, ppIB));
	
	return hr;
}
//--------------------------------------------------------------------------------------
// Create vertex layout
//--------------------------------------------------------------------------------------
HRESULT CD3DUtilityFunction::CreateVertexLayout(ID3D11Device* pd3dDevice, const D3D11_INPUT_ELEMENT_DESC layout[], int numElems, ID3DX11EffectTechnique* pTech, ID3D11InputLayout** ppLayout11)
{
	HRESULT hr = S_OK;

	D3DX11_PASS_SHADER_DESC VsPassDesc;
    D3DX11_EFFECT_SHADER_DESC VsDesc;

	if(!pTech->IsValid())
	{
#ifdef _DEBUG
		OUTPUTERRORINFO;
#endif
			return E_FAIL;
	}
	
	ID3DX11EffectPass* pPass = pTech->GetPassByIndex(0);
	
	if(!pPass->IsValid())
	{
#ifdef _DEBUG
		OUTPUTERRORINFO;
#endif
		return E_FAIL;
	}

	pPass->GetVertexShaderDesc(&VsPassDesc);
    VsPassDesc.pShaderVariable->GetShaderDesc(VsPassDesc.ShaderIndex, &VsDesc);

	SAFE_RELEASE(*ppLayout11);
    V_RETURN( pd3dDevice->CreateInputLayout( layout, numElems,
                                             VsDesc.pBytecode,
                                             VsDesc.BytecodeLength,
                                             ppLayout11 ) );
	return hr;
}

//--------------------------------------------------------------------------------------
// Debug function
//--------------------------------------------------------------------------------------
void CD3DUtilityFunction::PrintD3DXMatrix(const D3DXMATRIX& pMat)
{
	printf("\n");
	for(int j = 0; j < 4; j++){
		printf("\n");
		for(int i = 0; i < 4; i++){
			printf("%f,", pMat[j*4+i]);
		}
	}
}

//--------------------------------------------------------------------------------------
// Create absolute file name
//--------------------------------------------------------------------------------------
void CD3DUtilityFunction::CreateFileName(char pFileName[], unsigned int iSize, CDirectoryUtil* pDirUtil, enum SUBDIRECTORY iSubFolder, TCHAR* sFileName)
{
	TCHAR* dataFileDir = new TCHAR[iSize];
	pDirUtil->GetAbsoluteFileFolder(sFileName, iSubFolder, dataFileDir, iSize);
	// Conversion
	unsigned int tSize;
	wcstombs_s(&tSize, pFileName, iSize, dataFileDir, iSize);

	SAFE_DELETE_ARRAY(dataFileDir);
}

//--------------------------------------------------------------------------------------
//Lerp matrices function
//--------------------------------------------------------------------------------------
D3DXMATRIX CD3DUtilityFunction::LerpMatrix(D3DXMATRIX& a, D3DXMATRIX& b, float s)
{
	//Decompose a
	D3DXVECTOR3 t1, s1;
	D3DXQUATERNION q1;
	D3DXMatrixDecompose(&s1, &q1, &t1, &a);

	//Decompose b
	D3DXVECTOR3 t2, s2;
	D3DXQUATERNION q2;
	D3DXMatrixDecompose(&s2, &q2, &t2, &b);

	//Lerp each component
	D3DXVECTOR3 ti;
	D3DXVec3Lerp(&ti, &t1, &t2, s);

	D3DXVECTOR3 si;
	D3DXVec3Lerp(&si, &s1, &s2, s);

	D3DXQUATERNION qi;
	D3DXQuaternionSlerp(&qi, &q1, &q2, s);

	//Recompose each component
	D3DXMATRIX Tr, Sc, Rt;

	D3DXMatrixTranslation(&Tr, ti.x, ti.y, ti.z);
	D3DXMatrixScaling(&Sc, si.x, si.y, si.z);
	D3DXMatrixRotationQuaternion(&Rt, &qi);

	//Product
	D3DXMATRIX p = Sc * Rt * Tr;

	//Return
	return p;
}

//--------------------------------------------------------------------------------------
//Create D3D9 device for use
//--------------------------------------------------------------------------------------
HRESULT CD3DUtilityFunction::CreateD3D9Device(IDirect3DDevice9** ppd3dDevice)
{
	if(*ppd3dDevice!=NULL)
	{
#ifdef _DEBUG
		OUTPUTERRORINFO;
#endif
			return E_FAIL;
	}

	*ppd3dDevice = DXUTGetD3D9Device();
	//no d3d9device has been created!
	if(*ppd3dDevice == NULL)
	{
		/*IDirect3D9* pD3D = DXUTGetD3D9Object();
		assert( pD3D != NULL );

		DXUTDeviceSettings d3dDeviceSettings = DXUTGetDeviceSettings();
		D3DCAPS9 d3dCAPS9;
		HRESULT hr = DXUTGetD3D9DeviceCaps( &d3dDeviceSettings, &d3dCAPS9);	
		hr = pD3D->CreateDevice( d3dDeviceSettings.d3d9.AdapterOrdinal, D3DDEVTYPE_NULLREF,
			DXUTGetHWNDFocus(), d3dDeviceSettings.d3d9.BehaviorFlags,
			&d3dDeviceSettings.d3d9.pp, ppd3dDevice );

		if(FAILED(hr))
			printf("Create the D3D9 device FAILED!\n");	*/
		D3DDISPLAYMODE Mode;
		IDirect3D9* pD3D9 = DXUTGetD3D9Object();
		pD3D9->GetAdapterDisplayMode(0, &Mode);

		D3DPRESENT_PARAMETERS pp;
		ZeroMemory( &pp, sizeof(D3DPRESENT_PARAMETERS) ); 
		pp.BackBufferWidth  = 1;
		pp.BackBufferHeight = 1;
		pp.BackBufferFormat = Mode.Format;
		pp.BackBufferCount  = 1;
		pp.SwapEffect       = D3DSWAPEFFECT_COPY;
		pp.Windowed         = TRUE;

		//!!!Add D3DCREATE_FPU_PRESERVE --> Ensure 32bit precision!!! instead of 24bit!!!
		if( FAILED( pD3D9->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_NULLREF, DXUTGetHWND(), D3DCREATE_HARDWARE_VERTEXPROCESSING|D3DCREATE_FPU_PRESERVE, &pp, ppd3dDevice ) ) )
		{
			ERR_OUT( L"Unable to create NULLREF D3D9 device!" );
			return E_FAIL;
		}		
	}
}
//--------------------------------------------------------------------------------------
//Initialize materials
//--------------------------------------------------------------------------------------
void CD3DUtilityFunction::InitMaterial( Material* pMaterial )
{
	ZeroMemory( pMaterial, sizeof( Material ) );

	pMaterial->vAmbient = D3DXVECTOR3( 0.2f, 0.2f, 0.2f );
	pMaterial->vDiffuse = D3DXVECTOR3( 0.8f, 0.8f, 0.8f );
	pMaterial->vSpecular = D3DXVECTOR3( 1.0f, 1.0f, 1.0f );
	pMaterial->fShininess = 0.0f;
	pMaterial->fAlpha = 1.0f;
	pMaterial->bSpecular = false;
	pMaterial->pTexture = NULL;
}

//--------------------------------------------------------------------------------------
//Compute the triangle area using heron's formula
//--------------------------------------------------------------------------------------
//Geometry
float CD3DUtilityFunction::ComputeTriangleArea(const D3DXVECTOR3& v1, const D3DXVECTOR3& v2, const D3DXVECTOR3& v3)
{
	float halfp;
	float a, b, c;
	D3DXVECTOR3 v2v1 = v2-v1;
	D3DXVECTOR3 v3v2 = v3-v2;
	D3DXVECTOR3 v1v3 = v1-v3;
	a = D3DXVec3Length(&v2v1);
	b = D3DXVec3Length(&v3v2);
	c = D3DXVec3Length(&v1v3);

	halfp = (a + b + c) * 0.5f;
	return (float) sqrt(halfp * (halfp - a) * (halfp - b) * (halfp - c));
}
//--------------------------------------------------------------------------------------
//Generate 1D NDF Plot Data bWhichAxis = true --> Axis X, bWhichAxis = false --> Axis Y.
//--------------------------------------------------------------------------------------
bool CD3DUtilityFunction::Save1DPlotData(char* str1DPlotFile, D3DXVECTOR3* pNormalData, unsigned int iNumNormal, bool bWhichAxis)
{
	if (str1DPlotFile == NULL||pNormalData==NULL||iNumNormal<=0)
		return false;
	
	//-90 to 90, 1 degree interval
	float* pNDFPlot = new float[180];
	ZeroMemory(pNDFPlot,sizeof(float)*180);

	for (int i = 0; i < iNumNormal; i++)
	{
		D3DXVECTOR3 norm = *(pNormalData+i);

		if(bWhichAxis) //Axis X
		{
			if(fabs(norm.y) > EPSILON*NDFPLOTSCALE)
				continue;
		}
		else //Axis Y
		{
			if(fabs(norm.x) > EPSILON*NDFPLOTSCALE)
				continue;
		}

		float fThetaM = acosf(norm.z);
		if(bWhichAxis) //Axis X
		{
			if(norm.x < 0) 
				fThetaM *= -1;
		}
		else //Axis Y
		{
			if(norm.y < 0) 
				fThetaM *= -1;
		}
		
		//convert radian to degree
		fThetaM *= RADIAN2DEGREE;

		int idxArr = floor(fThetaM+0.5f) + 90 - 1; //index
		//printf("%d \n", idxArr);
		pNDFPlot[idxArr] += 1.0f;
	}

	//save plot files
	float sumNDF = 0;
	for (int i = 0; i < 180; i++)
	{
		sumNDF += pNDFPlot[i];
	}

	FILE* fp = fopen(str1DPlotFile,"w");
	for (int i = 0; i < 180; i++)
	{
		if(pNDFPlot[i] > EPSILON)
		{
			int newidx = (i+1) - 90;
			fprintf(fp,"%d %f\n",newidx,(pNDFPlot[i]/sumNDF)); //normalize ndf into [0,1]
		}
	}
	fclose(fp);

	SAFE_DELETE_ARRAY(pNDFPlot);
	return true;

}
//--------------------------------------------------------------------------------------
//Generate NDF image -->2D projection distribution of normals
//--------------------------------------------------------------------------------------
HRESULT CD3DUtilityFunction::SaveNDFImage(ID3D11Device* pd3dDevice, WCHAR* sFileName, unsigned int iWidth, unsigned int iHeight, 
	                                      D3DXVECTOR3* pNormalData, unsigned int iNumNormal, float fNormalizeFactor)
{
	HRESULT hr = S_OK;
	if(pNormalData == NULL)
		return E_FAIL;
	//loop all normals
	float* pAccumBuf = new float[iWidth*iHeight];
	memset(pAccumBuf,0,sizeof(float)*iWidth*iHeight);
	for (int i = 0; i < iNumNormal; i++)
	{
		D3DXVECTOR3 norm = *(pNormalData+i);
		norm.x = norm.x/2.0f + 0.5f; //(-1,1) --> (0,1); 
		norm.y = norm.y/2.0f + 0.5f;
		int iPosX = floorf(norm.x * iWidth);
		int iPosY = floorf(norm.y * iHeight);
		unsigned int idx = iPosY*iWidth + iPosX;
		pAccumBuf[idx] += 1.0f;
	}

	float fMaxVal = DBL_MIN;
	for (int j = 0; j < iHeight; j++)
		for(int i = 0; i < iWidth; i++)
	{
		//printf("%f\t", pAccumBuf[j*iWidth+i]);
		if(pAccumBuf[j*iWidth+i] > fMaxVal)
			fMaxVal = pAccumBuf[j*iWidth+i];
	}

	
	// Create texture
	D3D11_TEXTURE2D_DESC TexDesc;
	TexDesc.Width              = iWidth;
	TexDesc.Height             = iHeight;
	TexDesc.MipLevels          = 1;
	TexDesc.ArraySize          = 1;
	TexDesc.Format             = DXGI_FORMAT_R8G8B8A8_UNORM;
	
	DXGI_SAMPLE_DESC RealSampleDesc;
	RealSampleDesc.Count = 1;
	RealSampleDesc.Quality = 0;
	TexDesc.SampleDesc         = RealSampleDesc;

	TexDesc.Usage              = D3D11_USAGE_DYNAMIC;
	TexDesc.BindFlags          = D3D11_BIND_SHADER_RESOURCE;
	TexDesc.CPUAccessFlags     = D3D11_CPU_ACCESS_WRITE;
	// If they request mipmap levels, it's nice to be able to autogenerate them.
	TexDesc.MiscFlags          = 0;

	ID3D11Texture2D* pTex;
	V(pd3dDevice->CreateTexture2D(&TexDesc, NULL, &pTex));
	// Update the description with the read number of mipmaps, etc.
	pTex->GetDesc(&TexDesc);
	//
	// map for writing
	ID3D11DeviceContext* pd3dImmediateContext;
	pd3dDevice->GetImmediateContext(&pd3dImmediateContext);
	D3D11_MAPPED_SUBRESOURCE MappedResource; 
	V(pd3dImmediateContext->Map(pTex, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource));

	RGBA8Bytes* pPixel = (RGBA8Bytes*)(MappedResource.pData);
	for (int j = 0; j < iHeight; j++)
		for(int i = 0; i < iWidth; i++)
	{
		unsigned int idx = j*iWidth + i;
		if(pAccumBuf[idx] < 0.00001f)//==0
		{
			(pPixel + idx)->r = 0;
			(pPixel + idx)->g = 0;
			(pPixel + idx)->b = 0;
			(pPixel + idx)->a = 255;
		}
		else{
			D3DXVECTOR3 color = GetColour(pAccumBuf[idx],0,fMaxVal);
			(pPixel + idx)->r = color.x * 255;
			(pPixel + idx)->g = color.y * 255;
			(pPixel + idx)->b = color.z * 255;
			(pPixel + idx)->a = 255;
		}
		
	}
	pd3dImmediateContext->Unmap(pTex,0);
	V(D3DX11SaveTextureToFile(pd3dImmediateContext,pTex,D3DX11_IFF_BMP,sFileName));
	
	// finish writing	
	SAFE_RELEASE(pTex);
	SAFE_RELEASE(pd3dImmediateContext);
	SAFE_DELETE_ARRAY(pAccumBuf);

	return hr;
}


//--------------------------------------------------------------------------------------
//   Return a RGB colour value given a scalar v in the range [vmin,vmax]
//   In this case each colour component ranges from 0 (no contribution) to
//   1 (fully saturated), modifications for other ranges is trivial.
//   The colour is clipped at the end of the scales if v is outside
//   the range [vmin,vmax]
//   
//--------------------------------------------------------------------------------------
D3DXVECTOR3 CD3DUtilityFunction::GetColour(float v,float vmin,float vmax)
{
   D3DXVECTOR3 c = D3DXVECTOR3(1.0,1.0,1.0); // white
   float dv;

   if (v < vmin)
      v = vmin;
   if (v > vmax)
      v = vmax;
   dv = vmax - vmin;

   if (v < (vmin + 0.25 * dv)) {
      c.x = 0;
      c.y = 4 * (v - vmin) / dv;
   } else if (v < (vmin + 0.5 * dv)) {
      c.x = 0;
      c.z = 1 + 4 * (vmin + 0.25 * dv - v) / dv;
   } else if (v < (vmin + 0.75 * dv)) {
      c.x = 4 * (v - vmin - 0.5 * dv) / dv;
      c.z = 0;
   } else {
      c.y = 1 + 4 * (vmin + 0.75 * dv - v) / dv;
      c.z = 0;
   }
   
   return(c);
}

//--------------------------------------------------------------------------------------
//Save Framebuffer image
//--------------------------------------------------------------------------------------
HRESULT  CD3DUtilityFunction::SaveFrameBufferToFile( ID3D11DeviceContext *pContext, WCHAR* sFileName,  enum D3DX11_IMAGE_FILE_FORMAT format)
{
	HRESULT hr = S_OK;
	//Save Frame Buffer
	// Extract the actual back buffer
	ID3D11Texture2D* pBackBuffer;
	DXUTGetDXGISwapChain()->GetBuffer( 0, __uuidof( ID3D11Texture2D ), reinterpret_cast< void** >( &pBackBuffer ) );

	// Save the backbuffer directly
	if( FAILED( D3DX11SaveTextureToFile(pContext,pBackBuffer, format , sFileName ) ) )
	{
		printf( "Unable to save back-buffer texture to file!\n" );
		return E_FAIL;
	}

	SAFE_RELEASE(pBackBuffer);
	return hr;
}

//--------------------------------------------------------------------------------------
// Create a screen quad for render-to-texture
//--------------------------------------------------------------------------------------
HRESULT CD3DUtilityFunction::CreateScreenQuadVB(ID3D11Device* pd3dDevice, ID3D11Buffer** ppQuadVB)
{
	HRESULT hr = S_OK;

	UINT uiVertBufSize = 4*sizeof(ScreenVertex2DTex);
	ScreenVertex2DTex pVerts[4];
	pVerts[0].pos_proj = D3DXVECTOR3( -1, -1, 0.5f );
	pVerts[0].uv.x = 0;
	pVerts[0].uv.y = 1.0f;
	pVerts[1].pos_proj = D3DXVECTOR3( -1, 1, 0.5f );
	pVerts[1].uv.x = 0;
	pVerts[1].uv.y = 0.0f;
	pVerts[2].pos_proj = D3DXVECTOR3( 1, -1, 0.5f );
	pVerts[2].uv.x = 1.0f;
	pVerts[2].uv.y = 1.0f;
	pVerts[3].pos_proj = D3DXVECTOR3( 1, 1, 0.5f );
	pVerts[3].uv.x = 1.0f;
	pVerts[3].uv.y = 0.0f;

	D3D11_BUFFER_DESC vbdesc =
	{
		uiVertBufSize,
		D3D11_USAGE_IMMUTABLE,
		D3D11_BIND_VERTEX_BUFFER,
		0,
		0
	};

	D3D11_SUBRESOURCE_DATA InitData;
	InitData.pSysMem = pVerts;
	InitData.SysMemPitch = 0;
	InitData.SysMemSlicePitch = 0;

	SAFE_RELEASE(*ppQuadVB);
	hr = pd3dDevice->CreateBuffer( &vbdesc, &InitData, ppQuadVB);

	return hr;
}

//--------------------------------------------------------------------------------------
// Compute the Fresnel term using schlick approximation
//F(f_0) = f_0 + (1-f_0)(1-dot(l,h))^5; f_0 = ((1-n)/(1+n))^2
//--------------------------------------------------------------------------------------
float CD3DUtilityFunction::ComputeFresnelTermUsingSchlick(float fRefractiveIndex, const D3DXVECTOR3& vInVec, const D3DXVECTOR3& vHalfVec)
{
	float f_0 = (1-fRefractiveIndex)/(1+fRefractiveIndex);
	f_0 = f_0*f_0;

	float dotVal = D3DXVec3Dot(&vInVec,&vHalfVec);
	dotVal = 1 - dotVal;
	float dotVal2 = dotVal*dotVal;
	dotVal2 = dotVal2*dotVal2;
	dotVal = dotVal*dotVal2;//^5
	
	float ret = f_0 + (1-f_0)*dotVal;
	return ret;
}